/*
 * $AIST_Release: 0.9.0 $
 * Copyright 2011 Information Technology Research Institute, National
 * Institute of Advanced Industrial Science and Technology
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package parser.analyzer

import scala.collection.mutable.{Map => MMap}
import scala.collection.mutable.{Set => MSet}
import scala.collection.JavaConversions._
import scala.util.parsing.input.Position

import com.google.protobuf.Descriptors._

import parser.DataTypes._
import parser.Nodes._
import parser.Results._
import parser.TreeWalker

/**
 * Reads ProtocolBuffers IDL 
 */
object ReadProto {

  case class State(pbFiles: List[FileDescriptor], pos: Map[String, Position])

  /**
   * Reads the ProtocolBuffers IDLs which the specified AST contains.
   */
  def read(ast: List[Stmt]): Result[State] = {
    val pbFiles  = MMap[String, FileDescriptor]()
    val pos      = MMap[String, Position]()
    val topFiles = MSet[String]()

    def readProto(s: ProtoStmt): Result[Stmt] = {
      val ProtoStmt(fn) = s

      if (topFiles.contains(fn)) {
        failure(s.pos, "\"%s\" already has been read.", fn)
      } else {
        topFiles += fn
        PBReader.read(s.pos, fn).flatMap { fds => 
          val r = fds.map { fd =>
            pbFiles.get(fd.getName) match {
              case None =>
                pbFiles(fd.getName) = fd
                pos(fd.getName)     = s.pos
                Success(Unit)
              case Some(readFd) =>
                if (readFd == fd) Success(Unit) // Do nothing
                else              failure(s.pos, "Proto file \"%s\" is defferent from the read proto file of same name.")
            }
          }
          mergeList(r).mapConst(s)
        }
      }
    }

    val r = ast.map { x => 
      x match {
        case s: ProtoStmt => readProto(s)
        case s =>
          TreeWalker.walkTree(classOf[Node], s) {
            case s: ProtoStmt =>
              failure(s.pos, "The proto stemtment is not on top level.")
          }
      }
    }
    mergeList(r).mapConst(State(pbFiles.values.toList, pos.toMap))
  }
}
